package com;

import javax.jws.Oneway;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class MiniQueryRunner extends MiniAbstractQueryRunner{
    //每个子类的构造函数一定是会调用父类的某个构造函数的
    public MiniQueryRunner() {
        super();
    }

    /*
    * QueryRunner的构造函数，它控制ParameterMetaData的使用。
    * 如果pmdKnownBroken设置为true，我们甚至不会尝试它;如果错误，我们将尝试它，如果它坏了，我们将记住不再使用它
    */
    public MiniQueryRunner(boolean pmdKnownBroken){
        super(pmdKnownBroken);
    }

    public MiniQueryRunner (DataSource ds){
        super(ds);
    }

    public MiniQueryRunner(DataSource ds, boolean pmdKnownBroken) {
        super(ds, pmdKnownBroken);
    }

    //写类库：把麻烦留给自己，把方便留给别人，解决sql语句中没有问号（无参数delete from xx where id = 5）
    //写多种情况，方便根据需要直接调用
    public int update(Connection con,String sql) throws SQLException {
        return this.update(con,false,sql,(Object[])null);
    }

    public int update(Connection con,String sql,Object param) throws SQLException {
        return this.update(con,false,sql,new Object[]{param});
        //最后一个参数值转换
    }

    //可变长度的参数其实是个数组，所以上面的方法，先转换为数组
    // 按照现在的知识，前面的2个方法是多余的，可以删掉 （经测试，删掉确实可以）
    public int update(Connection con,String sql,Object...params) throws SQLException {
        return update(con,false,sql,params);
    }

    //下面是的三个update方法都是没有Connection的，要先获取
    // 没有传递Connection的情况，是靠调用父类prepareConnection DataSource来获取连接
    // 采取的方式是自己执行完update之后自己关闭
    public int update(String sql) throws SQLException {
        Connection con = this.prepareConnection();
        return this.update(con, true, sql, (Object[]) null);
    }

    public int update(String sql,Object param) throws SQLException {
        Connection con=this.prepareConnection();
        return this.update(con,true,sql,new Object[]{param});
    }

    public int update(String sql, Object...params) throws SQLException {
        Connection con=this.prepareConnection();
        return this.update(con,true,sql,params);
    }

    //所有参数都有的 ，其它update方法都是重写这个
    private int update(Connection con,boolean closeCon,String sql,Object ... params)throws SQLException{
        // 为了代码健壮（robust），先进行一系列的检查，守护代码，卫士代码
        if(con == null){
            throw new SQLException("Null Connection");
        }
        if(sql == null){
            if(closeCon){
                close(con);
            }
            throw new SQLException("Null SQL statement");
        }
        PreparedStatement stmt =null;
        int rows=0;
        try {
            stmt = this.prepareStatement(con,sql);
            this.fillStatement(stmt,params);
            rows=stmt.executeUpdate(); //这边是关键

        } catch (SQLException e) {
            this.rethrow(e,sql,params);
        }finally {
            close(stmt);
            if(closeCon){
                close(con);
            }
        }
        return rows;
    }


    public <T> T query(String sql,MiniResultSetHanlder<T> rsh,Object... params) throws SQLException {
        Connection con=this.prepareConnection();
        return this.query(con,true,sql,rsh,params);
    }

    public <T> T query(String sql,MiniResultSetHanlder<T> rsh) throws SQLException {
        Connection con =this.prepareConnection();
        return this.query(con,true,sql,rsh,(Object[]) null);
    }

    private <T> T query(Connection con,boolean closeCon,String sql,MiniResultSetHanlder rsh,Object... params) throws SQLException {

        if(con == null){
            throw new SQLException("Null connection");
        }
        if(sql == null){
            throw new SQLException("Null SQL statement");
        }
        if(rsh == null){
            if(closeCon){
                close(con);
            }
            throw new SQLException("Null ResultSetHandler");
        }

        PreparedStatement stmt = null;
        ResultSet rs = null;
        T result = null;

        try{
            stmt = this.prepareStatement(con,sql);
            this.fillStatement(stmt,params);
            rs = stmt.executeQuery();
            result = (T) rsh.handle(rs);
        } catch (SQLException e) {
            this.rethrow(e,sql,params);
        }finally {
            try {
                close(rs);
            }finally {
                close(stmt);
                if(closeCon){
                    close(con);
                }
            }
        }
        return result;
    }
}
